package cn.zjz.maven.upload;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.http.*;
import cn.hutool.log.StaticLog;
import org.apache.commons.cli.*;

import java.io.File;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;

public class MavenUpload {

    public static void main(String[] args) {
        MavenUpload mavenUpload = new MavenUpload();
        mavenUpload.mavenUpload(args);
    }

    final String HTTP_HEADER_AUTHORIZATION_VALUE_PREFIX = "Basic ";

    final String forceNo = "0";
    final String forceYes = "1";
    /**
     * 覆盖更新文件标识 0=不强制；1=强制
     */
    String force = forceNo;


    String authorizationValue = "";
    String username = "";
    String password = "";
    String remoteRepoHome = "";
    String localRepoHome = "";
    String localRepoHomeRelativePath = "";

    final int MIN_THREAD_SIZE = 3;
    final int MAX_THREAD_SIZE = 30;
    /**
     * 多线程，最大30
     */
    int threadSize = MIN_THREAD_SIZE;
    /**
     * 线程池
     */
    ThreadPoolExecutor threadPoolExecutor = null;

    /**
     * 文件上传
     * @param args 参数列表
     */
    public void mavenUpload(String[] args) {
        long s = System.currentTimeMillis();
        commandLine(args);
        preProcess();
        auth();

        String localPath = localRepoHome + localRepoHomeRelativePath;
        List<File> files = FileUtil.loopFiles(localPath, f -> f.getPath().endsWith(".jar")
                || f.getPath().endsWith(".pom") || f.getPath().endsWith(".sha1") || f.getPath().endsWith(".md5"));

        StaticLog.info("Search .jar and .pom files total: {}", files.size());

        CountDownLatch countDownLatch = new CountDownLatch(files.size());

        for (File file : files) {
            Runnable command = () -> {
                String fileLocation = file.getPath().substring(localRepoHome.length());
                String fileLocationUrl = fileLocation.replaceAll("\\\\", "/");
                String remoteUrl = remoteRepoHome + fileLocationUrl;
                // 强制更新或上传
                if (forceYes.equals(force)) {
                    fileUpload(remoteUrl, file);
                } else {
                    boolean fileExists = fileExists(remoteUrl);
                    if (!fileExists) {
                        fileUpload(remoteUrl, file);
                    }
                }
                countDownLatch.countDown();
            };
            threadPoolExecutor.execute(command);
        }

        try {
            countDownLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }

        threadPoolExecutor.shutdown();
        StaticLog.info("SHUTDOWN, time used {} ms", System.currentTimeMillis() - s);
    }

    /**
     * 文件上传
     * @param remoteUrl 资源地址
     * @param file 文件
     */
    void fileUpload(String remoteUrl, File file) {
        HttpResponse response = HttpUtil.createRequest(Method.PUT, remoteUrl)
                .header(Header.AUTHORIZATION, authorizationValue)
                .header(Header.CONTENT_TYPE, ContentType.OCTET_STREAM.getValue())
                .body(new FileResource(file)).execute();
        boolean created = response.getStatus() == HttpStatus.HTTP_CREATED;
        StaticLog.info("{} UPLOAD {} RESOURCE {}", Thread.currentThread().getName(), created, remoteUrl);
    }

    /**
     * 查看资源，不下载
     * @param remoteUrl 资源地址
     * @return 资源是否存在
     */
    boolean fileExists(String remoteUrl) {
        HttpResponse response = HttpUtil.createRequest(Method.HEAD, remoteUrl).header(Header.AUTHORIZATION, authorizationValue).execute();
        boolean fileExists = response.getStatus() == HttpStatus.HTTP_OK;
        StaticLog.info("{} EXISTS {} RESOURCE {}", Thread.currentThread().getName(), fileExists, remoteUrl);
        return fileExists;
    }

    /**
     * 身份认证
     */
    public void auth() {
        authorizationValue = HTTP_HEADER_AUTHORIZATION_VALUE_PREFIX + Base64.encode(username + ":" + password);
    }

    /**
     * 参数修改
     */
    void preProcess() {
        if (remoteRepoHome.endsWith("/")) {
            remoteRepoHome = remoteRepoHome.substring(0, remoteRepoHome.length() - 1);
        }
        if (localRepoHome.endsWith("\\")) {
            localRepoHome = localRepoHome.substring(0, localRepoHome.length() - 1);
        }
        if (localRepoHomeRelativePath.endsWith("\\")) {
            localRepoHomeRelativePath = localRepoHomeRelativePath.substring(0, localRepoHomeRelativePath.length() - 1);
        }
        if (!localRepoHomeRelativePath.startsWith("\\")) {
            localRepoHomeRelativePath = "\\" + localRepoHomeRelativePath;
        }
    }

    /**
     * 命令函参数
     * @param args 参数列表
     */
    void commandLine(String[] args) {
        String u = "u";
        String p = "p";
        String r = "r";
        String l = "l";
        String d = "d";
        String s = "s";
        String f = "f";
        Options options = new Options();
        options.addOption(Option.builder(u).longOpt("username").required(true).hasArg().desc("username").build());
        options.addOption(Option.builder(p).longOpt("password").required(true).hasArg().desc("password").build());
        options.addOption(Option.builder(r).longOpt("remoteRepoHome").required(true).hasArg().desc("Remote Repository Home").build());
        options.addOption(Option.builder(l).longOpt("localRepoHome").required(true).hasArg().desc("Local Repository Home").build());
        options.addOption(Option.builder(d).longOpt("localRepoHomeRelativePath").required(false).hasArg().desc("Local Repository Home Relative Path").build());
        options.addOption(Option.builder(s).longOpt("threadSize").required(false).hasArg().desc("thread size").build());
        options.addOption(Option.builder(f).longOpt("force").required(false).hasArg().desc("force update").build());

        try {
            CommandLineParser parser = new DefaultParser();
            CommandLine cli = parser.parse(options, args);

            username = cli.getOptionValue(u);
            password = cli.getOptionValue(p);

            remoteRepoHome = cli.getOptionValue(r);
            localRepoHome = cli.getOptionValue(l);
            localRepoHomeRelativePath = cli.getOptionValue(d);
            if (localRepoHomeRelativePath == null) {
                localRepoHomeRelativePath = "";
            }

            String threadSize = cli.getParsedOptionValue(s);
            if (threadSize != null) {
                int parseInt = Integer.parseInt(threadSize);
                this.threadSize = parseInt > MAX_THREAD_SIZE ? MAX_THREAD_SIZE : Math.max(parseInt, MIN_THREAD_SIZE);
            }
            this.threadPoolExecutor = ThreadUtil.newExecutor(this.threadSize);

            String force = cli.getParsedOptionValue(f);
            if (force != null) {
                this.force = force;
            }

        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

}
